
var server = false;

// Don't require server connection
var local = true;

var doUpdateEntities = true;
var ws, mq;

var myId = 666;

Game = function(state, view, groundMap, time) {
    this.state = state;
    this.view = view;
    this.groundMap = groundMap;
    this.time = time;
};

// DEBUG
var prevT = Date.now();
var prevDt = 0;

var gameloop = function(game, logIt) {

    while (game.time.hasTick()) {

        // DEBUG - printing fps
        var t = Date.now();
        var Dt = t - prevT;
        if (Dt > prevDt)
            prevDt = Dt;
        else
            prevDt = Math.min(100, prevDt*0.995);
        $("#DEBUG").text(Math.round(prevDt) + " ms, " + Math.round(1000/prevDt) + " fps");
        prevT = t;

	    step(game, game.time.getTick());

	    updateView(game.view);

        if (game.time.getFrameId() % 6 == 0 && !local) {
            ws.send(JSON.stringify({
                cmd: 'moves',
                moves: mq.peek()
            }));
        }

        var i, n, ents = game.state.entities;
        var deadEnts = [];
        for (i = 0, n = ents.length; i < n; ++i) {
        	if (!ents[i].alive) {
        	    deadEnts.push(ents[i]);
        	}
        }
        for (i = 0, n = deadEnts.length; i < n; ++i) {
        	game.state.remove(deadEnts[i]);
        	game.view.remove(deadEnts[i].view);
        }

    }
    requestAnimationFrame( function() {gameloop(game); } );
};

var main = function() {
    
    var view = initView();

    var state = new State();

    var heightmap = new HeightMap(400 * 7, 32 * 7);

     game = new Game(state, view, heightmap, new Time());

    // Tunnels shouldn't be created here, but in 
    // some 'World' instance shared with server code

    var tunnelSolids = createTunnel( heightmap, 
                                     Vec(90, 28, -40),
                                     Vec(430, 28, -40) );
    var tunnel = CompositeSolid( tunnelSolids );                     

    // This tests level of detail stuff.
    // To be continued ...

    var D = 1;
    for (var i = -D; i <= D; i++) {
        for (var j = -D; j <= D; j++) {
            var LOD = 1; //Math.min(3, Math.abs(i) + Math.abs(j));
            game.view.scene.add( createGroundView(heightmap, i*400, j*400, 
                                        LOD ? LOD : 1, tunnel.getPhyObj() ) );
        }
    }

    var car;
    
    // Playing around with server connection

    function startGame(msg) {
        game.time.frameId = msg.frameId;
        myId = msg.clientId || myId;
        game.time.prevT = Date.now();
        car = new Car(2.5, 7.5, 5, myId);
        state.add(car);
        view.add( CarView(car) );
        view.add( CarView2(car) );

        // Just for fun...
        var solids = createBridge( heightmap,
                                   Vec(20, null, 20), 
                                   Vec(160, null, 10),
                                   30, 40 );
        state.add( CompositeSolid(solids) );
        view.add( SolidView(solids) );

        // Just for more fun...
        solids = createBridge( heightmap,
                               Vec(80, null, 200), 
                               Vec(80, null, -25),
                               30, 30 );
        state.add( CompositeSolid(solids) );
        view.add( SolidView(solids) );

        state.add( tunnel );
        view.add( SolidView(tunnelSolids) );

        view.add( new Cam(car, heightmap, state.entities, view.camera) )

	
	var portal1 = new TelePortal(Vec(-200, heightmap.getHeight(80, -200), 80 ), true, Vec(1, 0, 0));
	var portal2 = new TelePortal(Vec(180, heightmap.getHeight(200, 180), 200 ), true, Vec(1, 0, 0));
	var portal3 = new TelePortal(Vec(-180, heightmap.getHeight(280, -180), 280 ), true, Vec(1, 0, 0));
	
	portal1.setForwardPortal(portal2);
	portal2.setForwardPortal(portal3);
	portal3.setForwardPortal(portal1);
	
	portal1.setBackwardPortal(portal3);
	portal2.setBackwardPortal(portal1);
	portal3.setBackwardPortal(portal2);
	
	state.add( CompositeSolid(portal1.solids, portal1) );
	state.add( CompositeSolid(portal2.solids, portal2) );
	state.add( CompositeSolid(portal3.solids, portal3) );
	
	view.add( SolidView(portal1.solids) );
	view.add( SolidView(portal2.solids) );
	view.add( SolidView(portal3.solids) );
	
        gameloop(game, true);
    }
    
    function testBullet() {
	var p = car.centerVec;
	var d = car.forwardVec;
	var b = new SimpleBullet(p.x, p.y, p.z, d.x, d.y, d.z);
	var bv = new SimpleBulletView(b);
	b.view = bv; // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
	state.add(b);
	view.add(bv);
    }
    
    ws = new WebSocket('ws://localhost:4444');

    ws.onopen = function() {
        game.socket = ws;
        console.log('Connected');
    };
    
    ws.onmessage = function(msg) {
        var m = JSON.parse(msg.data),
	    st, i, n, sent, cent, newEntity, po, cpo;
        if (m.m === 'init') {
	    startGame(m);
        } else if (m.m === 'st') {
	    // fool around with old data
	    st = m.state;
	    for (i = 0, n = st.length; i < n; ++i) {
		sent = st[i];
		cent = state.getEntityById(sent.id);
		
		if (!cent) {
		    //Yeah, it's a car
		    cent = new Car(0, 10, 0, sent.id);
		    state.add(cent);
		    view.add( CarView(cent) );
		    view.add( CarView2(cent) );
		    
		}
		
		// Just to see
		if ((cent.id) && cent.id !== myId) {
		    po = sent.po;
		    
		    cpo = cent.phyObj;
		    for (i = 0; i < po.points.length; ++i) {
			
			cpo.points[i].pos.set(po.points[i].pos.x,po.points[i].pos.y,po.points[i].pos.z);
			cpo.points[i].vel.set(po.points[i].vel.x,po.points[i].vel.y,po.points[i].vel.z);
			cpo.points[i].acc.set(po.points[i].acc.x,po.points[i].acc.y,po.points[i].acc.z);
			cpo.points[i].groundContact = po.points[i].groundContact;
		    }
		    
		    // Constraints ARE correct...
		    
		    cent.forwardVec.set(sent.fv.x,sent.fv.y,sent.fv.z);
		    
		    
 		}
	    }
	}
    };

    ws.onclose = function() {
        console.log('Connection was closed');
    };

    ws.onerror = function() {
        console.log('onerror was called');
    };
    
    var lastCmdSent = {}, nextCmd;
    
    mq = new MessageQueue();
    
    $(document).keydown(function(e) {
        e.preventDefault();
        var k = 0;
        switch (e.keyCode) {
            case 37:
                k = 4;
                break;
            case 38:
                k = 1;
                break;
            case 39:
                k = 8;
                break;
            case 40:
                k = 2;
                break;
            default:
                break;
        }
        if (k > 0) {
            if (lastCmdSent.cmd != 'keydown' ||
                lastCmdSent.key != k) {
                
                lastCmdSent = {
                    cmd: 'keydown',
                    key: k,
                    frameId: game.time.getFrameId()
                };
                car.setKey(k, game.time.getFrameId());
                mq.add(lastCmdSent);
            }
        }
    }).keyup(function(e) {
        var k = 0;
        switch (e.keyCode) {
            case 37:
                k = 4;
                break;
            case 38:
                k = 1;
                break;
            case 39:
                k = 8;
                break;
            case 40:
                k = 2;
                break;
	    case 32:
		testBullet();
		break;
            default:
                break;
        }
        if (k > 0) {
            if (lastCmdSent.cmd != 'keyup' ||
                lastCmdSent.key != k) {
                
                lastCmdSent = {
                    cmd: 'keyup',
                    key: k,
		    frameId: game.time.getFrameId()
                };
                car.unsetKey(k, game.time.getFrameId());
                mq.add(lastCmdSent);
            }
        }
    });
    
    if (local) {
	startGame({frameId:0});
    }
};

$(document).ready(main);
